//-----------------------------------------------------------------------------
//           Name: ogles2_main.cpp
//         Author: Kevin Harris
//  Last Modified: 10/28/10
//    Description: This sample demonstrates how to initialize OpenGL ES 2.0.
//-----------------------------------------------------------------------------

#include <stdio.h>
#include <windows.h>
#include <TCHAR.h>

#include <windowsx.h>

// This sample uses the PowerVR OpenGL ES 2.0 Emulator SDK, to compile and 
// emulate OpenGL ES 2.0 code on Windows XP/Vista machines. Therefore, you'll 
// need download, and install this SDK to gain access to the headers and 
// libraries needed to both compile and run this sample.
// http://www.imgtec.com/powervr/insider/sdkdownloads/index.asp
#include <EGL/egl.h>
#include <GLES2/gl2.h>

// istarAppDelegate
#include "core/runtime.h"
//#include "DeltaTimer.h"
//#include "ScreenUtils.h"
//#include "DebugConsole.h"
//#include "Renderer.h"
//#include "ResourceManager.h"
//#include "DisplayList.h"

//#include "types.h"

//#include "NativeAlertPopup.h"

//#include "GameApplication.h"
//#include "../Test/BasicApplication.h"

//-----------------------------------------------------------------------------
// GLOBALS
//-----------------------------------------------------------------------------

#define WINDOW_WIDTH	640
#define WINDOW_HEIGHT	480

EGLNativeWindowType	g_eglWindow = NULL;
EGLDisplay g_eglDisplay = NULL;
EGLConfig g_eglConfig = NULL;
EGLSurface g_eglSurface = NULL;
EGLContext g_eglContext = NULL;
HDC	g_hDC = NULL;

//external defined elsewhere
unsigned int glViewFrameBuffer = 0;
unsigned int glViewRenderBuffer = 0;

//-----------------------------------------------------------------------------
// PROTOTYPES
//-----------------------------------------------------------------------------
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine, int nCmdShow);

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

void init();
void shutDown();
void render();

bool applicationDidFinishLaunching();
void applicationWillResignActive();
void applicationDidBecomeActive();

//-----------------------------------------------------------------------------
// Name: WinMain()
// Desc: The application's entry point
//-----------------------------------------------------------------------------
int WINAPI WinMain( HINSTANCE hInstance,
                    HINSTANCE hPrevInstance,
                    LPSTR     lpCmdLine,
                    int       nCmdShow )
{
    // Register the windows class
    WNDCLASS winClass;
    winClass.style = CS_HREDRAW | CS_VREDRAW;
    winClass.lpfnWndProc = WindowProc;
    winClass.cbClsExtra = 0;
    winClass.cbWndExtra = 0;
    winClass.hInstance = hInstance;
    winClass.hIcon = 0;
    winClass.hCursor = 0;
    winClass.lpszMenuName = 0;
    winClass.hbrBackground = ( HBRUSH ) GetStockObject( WHITE_BRUSH );
    winClass.lpszClassName = _T( "MY_WINDOWS_CLASS" );
    unsigned int nWidth = WINDOW_WIDTH;
    unsigned int nHeight = WINDOW_HEIGHT;

    if( !RegisterClass(&winClass) )
    {
        MessageBox( 0, _T( "Failed to register the window class" ), _T( "Error" ), MB_OK | MB_ICONEXCLAMATION );
        return E_FAIL;
    }

    // Create the g_eglWindow.
    RECT sRect;
    SetRect( &sRect, 0, 0, nWidth, nHeight );
    AdjustWindowRectEx( &sRect, WS_CAPTION | WS_SYSMENU, false, 0 );
    nWidth = sRect.right - sRect.left;
    nHeight = sRect.bottom - sRect.top;

    g_eglWindow = CreateWindow( _T( "MY_WINDOWS_CLASS" ), _T( "Rawwar - OpenGL ES 2.0 emulation" ),
                           WS_VISIBLE | WS_CAPTION | WS_SYSMENU,
                           0, 0, nWidth, nHeight, NULL, NULL, hInstance, NULL );
    g_eglWindow = g_eglWindow;

    init(); //init OpenGL context

    applicationDidFinishLaunching();

    MSG uMsg;
    memset( &uMsg, 0, sizeof( uMsg ) );

    while( uMsg.message != WM_QUIT )
	{
		if( PeekMessage( &uMsg, NULL, 0, 0, PM_REMOVE ) )
		{
			TranslateMessage( &uMsg );
			DispatchMessage( &uMsg );
		}
        else
			render();
	}

    shutDown();

    UnregisterClass( "MY_WINDOWS_CLASS", winClass.hInstance );

	return uMsg.wParam;
}

bool isMouseDown = false;
bool isRightMouseDown = false;
int mousePosX = 0;
int mousePosY = 0;
DWORD timeMouseDown = 0;

//-----------------------------------------------------------------------------
// Name: WindowProc()
// Desc: The window's message handler
//-----------------------------------------------------------------------------
LRESULT CALLBACK WindowProc( HWND   hWnd, 
							 UINT   uMsg, 
							 WPARAM wParam, 
							 LPARAM lParam )
{
    switch( uMsg )
    {
        case WM_KEYDOWN:
		{
			switch( wParam )
			{
				case VK_ESCAPE:
					PostQuitMessage(0);
					break;
			}
		}
        break;

        // Here we are handling 2 system messages: screen saving and monitor power.
        // They are especially relevant on mobile devices.
        case WM_SYSCOMMAND:
        {
            switch( wParam )
            {
                case SC_SCREENSAVE: // Screensaver trying to start ?
                case SC_MONITORPOWER: // Monitor trying to enter powersave ?
                    return 0; // Prevent this from happening
            }
            break;
        }

        // Input events
        case WM_LBUTTONDOWN:
        case WM_LBUTTONUP:
        case WM_RBUTTONDOWN:
        case WM_RBUTTONUP:
        case WM_MOUSEMOVE:
            {
                int posX = GET_X_LPARAM(lParam); 
                int posY = GET_Y_LPARAM(lParam);
                switch (uMsg) {
                case WM_LBUTTONDOWN: {
                        isMouseDown = true;
                        timeMouseDown = GetTickCount();
                        //bcn::NativeInputEvent ev(bcn::NativeInputEvent::E_INPUT_DOWN, posX,posY);
                        //bcn::InputManager::Instance()->processInputEvent(ev);
                    } break;

                case WM_LBUTTONUP: {
                        isMouseDown = false;
                        DWORD elapsedTime = GetTickCount() - timeMouseDown;
                        timeMouseDown = 0;
                        //bcn::NativeInputEvent ev(bcn::NativeInputEvent::E_INPUT_UP, posX,posY, float(elapsedTime)/1000.f);
                        //bcn::InputManager::Instance()->processInputEvent(ev);
                    } break;

				case WM_RBUTTONDOWN:
					{
						isRightMouseDown = true;
						mousePosX = posX;
						mousePosY = posY;
					}
					break;

				case WM_RBUTTONUP:
					{
						isRightMouseDown = false;
						//bcn::NativeInputEvent ev(bcn::NativeInputEvent::E_INPUT_ROTATE,-10000,-10000);
						//ev.rotation = 0;
						//ev.isFinished = true;
						//bcn::InputManager::Instance()->processInputEvent(ev);
					}
					break;

                case WM_MOUSEMOVE:
                    if (isMouseDown) {
                        //bcn::NativeInputEvent ev(bcn::NativeInputEvent::E_INPUT_MOVE,posX,posY);
                        //bcn::InputManager::Instance()->processInputEvent(ev);
                    }
					else if (isRightMouseDown) {
						//bcn::NativeInputEvent ev(bcn::NativeInputEvent::E_INPUT_ROTATE,-10000,-10000);
						//ev.rotation = double(mousePosX - posX) * 0.005f;
						//bcn::InputManager::Instance()->processInputEvent(ev);
					}
					break;
                }

				mousePosX = posX;
				mousePosY = posY;
            }
            break;

		case WM_MOUSEWHEEL:
			{
				short zDelta = GET_WHEEL_DELTA_WPARAM(wParam);
				//bcn::NativeInputEvent ev(bcn::NativeInputEvent::E_INPUT_PINCH,-10000,10000);
				//ev.velocity = (zDelta / WHEEL_DELTA) * 10;
				//ev.factor = 1.f;
				//bcn::InputManager::Instance()->processInputEvent(ev);
			}
			break;

		case WM_CLOSE:
			{
				PostQuitMessage(0);	
			}
			break;

		case WM_DESTROY:
			{
				PostQuitMessage(0);
			}
			break;

		default:
		{
			return DefWindowProc( hWnd, uMsg, wParam, lParam );
		}
		break;
    }

    // Calls the default window procedure for messages we did not handle
    return DefWindowProc( g_eglWindow, uMsg, wParam, lParam );
}

//-----------------------------------------------------------------------------
// Name: init()
// Desc: 
//-----------------------------------------------------------------------------
void init()
{
    // Get the associated device context
    g_hDC = GetDC( g_eglWindow );

    if( !g_hDC )
    {
        MessageBox( 0, _T( "Failed to create the device context" ), _T( "Error" ), MB_OK|MB_ICONEXCLAMATION );
        return;
    }

    // Get the default display.

    g_eglDisplay = eglGetDisplay( g_hDC );

    if( g_eglDisplay == EGL_NO_DISPLAY )
        g_eglDisplay = eglGetDisplay( ( EGLNativeDisplayType ) EGL_DEFAULT_DISPLAY );

    // Initialize EGL.

    EGLint iMajorVersion, iMinorVersion;

    if( !eglInitialize( g_eglDisplay, &iMajorVersion, &iMinorVersion ) )
    {
        MessageBox( 0, _T( "eglInitialize() failed." ), _T( "Error" ), MB_OK|MB_ICONEXCLAMATION );
        return;
    }

	// Make OpenGL ES the current API.
	if (!eglBindAPI(EGL_OPENGL_ES_API))
	{
		MessageBox( 0, _T( "eglChooseConfig() failed." ), _T( "Error" ), MB_OK|MB_ICONEXCLAMATION );
		return;
	}

    // Specify the required configuration attributes.
    const EGLint pi32ConfigAttribs[] =
    {
        EGL_LEVEL,				0,
        EGL_SURFACE_TYPE,		EGL_WINDOW_BIT,
        EGL_RENDERABLE_TYPE,	EGL_OPENGL_ES2_BIT,
        EGL_NATIVE_RENDERABLE,	EGL_FALSE,
        EGL_DEPTH_SIZE,			24,//EGL_DONT_CARE,

		// antialias
		EGL_SAMPLE_BUFFERS, 1,
		EGL_SAMPLES, 4,

        EGL_NONE
    };

    // Find a config that matches all requirements.

    int iConfigs;
    if( !eglChooseConfig( g_eglDisplay, pi32ConfigAttribs, &g_eglConfig, 1, &iConfigs ) || ( iConfigs != 1 ) )
    {
        MessageBox( 0, _T( "eglChooseConfig() failed." ), _T( "Error" ), MB_OK|MB_ICONEXCLAMATION );
        return;
    }

    // Create a surface to draw to.

    g_eglSurface = eglCreateWindowSurface( g_eglDisplay, g_eglConfig, g_eglWindow, NULL );

    if( g_eglSurface == EGL_NO_SURFACE )
    {
        eglGetError(); // Clear error and try again.
        g_eglSurface = eglCreateWindowSurface( g_eglDisplay, g_eglConfig, NULL, NULL );
    }

    // Create a context.

    // Bind the API ( It could be OpenGLES or OpenVG )
    eglBindAPI( EGL_OPENGL_ES_API );

    EGLint ai32ContextAttribs[] =
    {
        EGL_CONTEXT_CLIENT_VERSION, 2,
        EGL_NONE
    };

    g_eglContext = eglCreateContext( g_eglDisplay, g_eglConfig, NULL, ai32ContextAttribs );

	// Bind the context to the current thread and use our window surface for 
    // drawing and reading.

    eglMakeCurrent( g_eglDisplay, g_eglSurface, g_eglSurface, g_eglContext );

	glClearColor( 0.0f, 1.0f, 0.0f, 1.0f );

	//createFrameBuffer
	
}

//-----------------------------------------------------------------------------
// Name: shutDown()
// Desc: 
//-----------------------------------------------------------------------------
void shutDown()	
{
    // Terminate OpenGL ES and destroy the window.
    eglMakeCurrent( g_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT );
    eglTerminate( g_eglDisplay );

    // Release the device context.
    if( g_hDC ) 
        ReleaseDC( g_eglWindow, g_hDC );

    // Destroy the window.
    if( g_eglWindow ) 
        DestroyWindow( g_eglWindow );
}

//-----------------------------------------------------------------------------
// Name: render()
// Desc: 
//-----------------------------------------------------------------------------
void render()	
{
	glClearColor(0.f, 0.f, 1.f, 1.f);
    glClear( GL_COLOR_BUFFER_BIT );
/*
    //drawFrame();
    bcn::display::deletePendentObjects();

    resourcemgr->update();

    //TODO- forward input events
    bcn::InputManager::Instance()->refreshDisplayList();
    
    // update
    static bcn::Chrono chrono;
    int delta = chrono.getTimeMS();
    chrono.start();

    bcn::display::getRoot()->logicTraversal(delta);

    // render
	bcn::renderer::instance->render(bcn::screen::width, bcn::screen::height);
*/
    // Swap Buffers.
    eglSwapBuffers( g_eglDisplay, g_eglSurface );
}

bool applicationDidFinishLaunching()
{
	kraw::Runtime::init();

/*    srand(timeGetTime());

    bcn::screen::width = WINDOW_WIDTH;
    bcn::screen::height = WINDOW_HEIGHT;
	bcn::screen::scaleFactor = 1.f;
	bcn::screen::displayInches = 7.f;

    bcn::DeltaTimer::init();
    bcn::screen::init();
    bcn::DebugConsole::init();
    bcn::renderer::init();
    bcn::resources::ResourceManager::init();
    bcn::display::initDisplayList();

    //application entry point
    //bcn::display::getRoot()->addChild(new rawwar::BasicApplication);
    bcn::display::getRoot()->addChild(new rawwar::LocalApplication);
	//bcn::display::getRoot()->addChild(new rawwar::GameApplication);
*/
    return true;
}

void applicationWillResignActive()
{
}

void applicationDidBecomeActive()
{
}
